using GriffinNav.Map.Data;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace GriffinNav.Map.VirtualEarth
{
    public class VirtualEarthMap: IMap
    {
        public event GenericEventHandler MapViewUpdated;
        public event MapErrorHandler OnMapError;

        private event TileBufferUpdatedHandler tileBufferUpdated;
        private bool mTileUpdatePending = false;

        private DataAccess mData = null;
        private MapInfo mMapInfo; // basic map information

        private SolidBrush mBackgroundBrush = new SolidBrush(Color.LightGray);
        
        private Bitmap mUpdateBuffer = null; // image buffer for pending map view updates
        private Bitmap mTileBuffer = null; // image buffer used to display on the screen
        private Bitmap mScreenBuffer = null;
        private Graphics mUpdateGraphics = null; // tile buffer update graphics
        private Graphics mScreenGraphics = null;

        private ImageAttributes mImgAttr = new ImageAttributes();
        
        private Rectangle mScreenRectangle = Rectangle.Empty; // screen dimensions
        private Rectangle mScreenDisplayArea = Rectangle.Empty; // portion of tile buffer to display on screen
        private Rectangle mScreenBufferZone = Rectangle.Empty; // buffer zone that triggers tile buffer update

        private Rectangle mTileBufferRectangle = Rectangle.Empty; // tile buffer dimensions
        private Rectangle mTileBufferTileBounds = Rectangle.Empty; // tile buffer tile boundaries
        private Rectangle mTileBufferPixelBounds = Rectangle.Empty; // tile buffer pixel boundaries
        
        private Rectangle mDetailLevelTileBounds = Rectangle.Empty; // detail level tile boundaries
        private Rectangle mDetailLevelPixelBounds = Rectangle.Empty; // detail level pixel boundaries

        public const int TILE_PIXEL_SIZE = 256; // pixel size of one tile
        public const int SCREEN_BUFFER_ZONE_SIZE = 64; // pixel size of screen buffer zone
        public const int BUFFER_WIDTH = 3; // tile size of tile buffer (BUFFER_WIDTH ^ 2 = number of tiles in tile buffer)

        private const int M_MAP_SHIFT_STEP = 16; // number of pixels to shift map in a single movement

        // current pixel position on the map
        private int mCurrentPixelX = 0; 
        private int mCurrentPixelY = 0;

        private int mDetailLevelIndex = 0; // current detail level

        public VirtualEarthMap(string filePath, Rectangle screenRectangle)
        {
            try
            {
                // set the screen size
                mScreenRectangle = screenRectangle;

                // subscribe to events
                tileBufferUpdated += new TileBufferUpdatedHandler(VirtualEarthMap_TileBufferUpdated);

                // set up the tile buffer images
                mTileBuffer = new Bitmap(TILE_PIXEL_SIZE * BUFFER_WIDTH, TILE_PIXEL_SIZE * BUFFER_WIDTH);
                mUpdateBuffer = new Bitmap(TILE_PIXEL_SIZE * BUFFER_WIDTH, TILE_PIXEL_SIZE * BUFFER_WIDTH);
                mUpdateGraphics = Graphics.FromImage(mUpdateBuffer);
                mTileBufferRectangle = new Rectangle(0, 0, TILE_PIXEL_SIZE * BUFFER_WIDTH, TILE_PIXEL_SIZE * BUFFER_WIDTH);

                mScreenBuffer = new Bitmap(screenRectangle.Width, screenRectangle.Height);
                mScreenGraphics = Graphics.FromImage(mScreenBuffer);
                
                // open map file
                mData = new DataAccess(filePath);
                mData.OnError += new MapErrorHandler(mData_OnError);

                // get the basic map info
                mMapInfo = mData.GetMapInfo();

                // convert the center long/lat into pixel coordinates
                VirtualEarthTileSystem.LatLongToPixelXY((double)mMapInfo.CenterLatitude, (double)mMapInfo.CenterLongitude, mMapInfo.DetailLevels[0], out mCurrentPixelX, out mCurrentPixelY);

                // set the area of tile buffer to show on the screen
                setScreenDisplayArea();

                // get the tile buffer
                startGetTileBuffer(mCurrentPixelX, mCurrentPixelY, mMapInfo.DetailLevels[0]);
            }
            catch (Exception ex)
            {
                throw new MapException(ex, "Error opening map file.", "VirtualEarthMap", "VirtualEarthMap()");
            }
        }

        private void mData_OnError(MapException ex, bool showMessage)
        {
            if (OnMapError != null)
            {
                OnMapError(ex, showMessage);
            }
        }

        #region Properties
        public MapInfo MapInfo
        {
            get
            {
                return mMapInfo;
            }
        }

        public Rectangle DisplayArea
        {
            get
            {
                return mScreenDisplayArea;
            }
        }

        public Rectangle BufferRectangle
        {
            get
            {
                return mTileBufferRectangle;
            }
        }

        public Rectangle ScreenRectangle
        {
            set
            {
                mScreenRectangle = value;

                mScreenBuffer = new Bitmap(mScreenRectangle.Width, mScreenRectangle.Height);
                mScreenGraphics = Graphics.FromImage(mScreenBuffer);

                setScreenDisplayArea();
            }
        }

        public Bitmap ScreenBuffer
        {
            get
            {
                return mScreenBuffer;
            }
        }

        public Graphics ScreenGraphics
        {
            get
            {
                return mScreenGraphics;
            }
        }

        public int PixelX
        {
            get
            {
                return mCurrentPixelX;
            }
        }

        public int PixelY
        {
            get
            {
                return mCurrentPixelY;
            }
        }

        public double Longitude
        {
            get
            {
                return 0;
            }
        }

        public double Latitude
        {
            get
            {
                return 0;
            }
        }

        public bool UpdatePending
        {
            get
            {
                return mTileUpdatePending;
            }
        }
        #endregion

        public void Open(string filePath)
        {
            
        }

        public void Open(string filePath, Size screenSize)
        {
            try
            {
                // open map file
                mData.Open(filePath);

                // get the basic map info
                mMapInfo = mData.GetMapInfo();

                mDetailLevelIndex = 0;

                // convert the center long/lat into pixel coordinates
                VirtualEarthTileSystem.LatLongToPixelXY((double)mMapInfo.CenterLatitude, (double)mMapInfo.CenterLongitude, mMapInfo.DetailLevels[0], out mCurrentPixelX, out mCurrentPixelY);

                // set the area of tile buffer to show on the screen
                setScreenDisplayArea();

                // get the tile buffer
                startGetTileBuffer(mCurrentPixelX, mCurrentPixelY, mMapInfo.DetailLevels[0]);

                // set the screen size
                mScreenRectangle = new Rectangle(0, 0, screenSize.Width, screenSize.Height);
            }
            catch (Exception ex)
            {
                throw new MapException(ex, "Error opening map file.", "VirtualEarthMap", "Open()");
            }
        }

        public void Close()
        {
            //if (mData != null)
            //{
            //    mData.Close();
            //}
        }

        private void startGetTileBuffer(int pixelX, int pixelY, int levelOfDetail)
        {
            try
            {
                // check for a pending update
                if (!mTileUpdatePending)
                {
                    // do not allow updates while this one is pending
                    mTileUpdatePending = true;

                    // start updating the tile buffer
                    ThreadPool.QueueUserWorkItem(new WaitCallback(getTileBuffer), new PixelPoint(pixelX, pixelY, levelOfDetail));
                }
            }
            catch (Exception ex)
            {
                throw new MapException(ex, "Error updating map view.", "VirtualEarthMap", "startGetTileBuffer()");
            }
        }

        private void getTileBuffer(object point)
        {
            try
            {
                Rectangle bufferTileBounds = Rectangle.Empty;
                Rectangle bufferPixelBounds = Rectangle.Empty;
                Rectangle detailTileBounds = Rectangle.Empty;
                Rectangle detailPixelBounds = Rectangle.Empty;
                int tileX = -1;
                int tileY = -1;

                // get coordinates for the tile that contains the supplied pixel coordinates
                VirtualEarthTileSystem.PixelXYToTileXY(((PixelPoint)point).X, ((PixelPoint)point).Y, out tileX, out tileY);

                // get the boundaries of the supplied detail level
                mData.GetDetailLevelBounds(((PixelPoint)point).LevelOfDetail, out detailTileBounds, out detailPixelBounds);

                // get the new tile buffer from the database
                mData.GetTileBuffer(tileX, tileY, ((PixelPoint)point).LevelOfDetail, ref mUpdateGraphics, BUFFER_WIDTH, TILE_PIXEL_SIZE, out bufferTileBounds, out bufferPixelBounds);

                if (tileBufferUpdated != null)
                {
                    // raise buffer updated event
                    tileBufferUpdated(bufferTileBounds, bufferPixelBounds, detailTileBounds, detailPixelBounds);
                }
            }
            catch(Exception ex)
            {
                if (OnMapError != null)
                {
                    OnMapError(new MapException(ex, "Error updating map view.", "VirtualEarthMap", "getTileBuffer()"), true);
                }
            }
        }

        public void DrawScreen()
        {
            try
            {
                if (Rectangle.Intersect(mScreenDisplayArea, mTileBufferRectangle) == mScreenDisplayArea)
                {
                    mScreenGraphics.DrawImage(mTileBuffer, mScreenRectangle, mScreenDisplayArea, GraphicsUnit.Pixel);
                }
                else if (Rectangle.Intersect(mScreenDisplayArea, mTileBufferRectangle) != mScreenDisplayArea)
                {
                    mScreenGraphics.FillRectangle(mBackgroundBrush, mScreenRectangle);
                    mScreenGraphics.DrawImage(mTileBuffer, mScreenRectangle, mScreenDisplayArea, GraphicsUnit.Pixel);
                }
            }
            catch (Exception ex)
            {
                throw new MapException(ex, "Error rendering map view.", "VirtualEarthMap", "DrawScreen()");
            }
        }

        public void DrawScreen(Bitmap arrowImage, Rectangle arrowRectangle)
        {
            try
            {
                if (Rectangle.Intersect(mScreenDisplayArea, mTileBufferRectangle) == mScreenDisplayArea)
                {
                    mScreenGraphics.DrawImage(mTileBuffer, mScreenRectangle, mScreenDisplayArea, GraphicsUnit.Pixel);
                }
                else if (Rectangle.Intersect(mScreenDisplayArea, mTileBufferRectangle) != mScreenDisplayArea)
                {
                    mScreenGraphics.FillRectangle(mBackgroundBrush, mScreenRectangle);
                    mScreenGraphics.DrawImage(mTileBuffer, mScreenRectangle, mScreenDisplayArea, GraphicsUnit.Pixel);
                }

                mImgAttr.SetColorKey(Color.White, Color.White);

                mScreenGraphics.DrawImage(arrowImage,
                    arrowRectangle, 0, 0, arrowImage.Width, arrowImage.Height,
                    GraphicsUnit.Pixel, mImgAttr);
            }
            catch (Exception ex)
            {
                throw new MapException(ex, "Error rendering map view.", "VirtualEarthMap", "DrawScreen()");
            }
        }

        private void VirtualEarthMap_TileBufferUpdated(Rectangle bufferTileBounds, Rectangle bufferPixelBounds, Rectangle detailLevelTileBounds, Rectangle detailLevelPixelBounds)
        {
            try
            {
                // allow another update to be requested
                mTileUpdatePending = false;

                mTileBufferTileBounds = bufferTileBounds;
                mTileBufferPixelBounds = bufferPixelBounds;
                mDetailLevelTileBounds = detailLevelTileBounds;
                mDetailLevelPixelBounds = detailLevelPixelBounds;

                // set the area of tile buffer to show on the screen
                setScreenDisplayArea();

                // copy update buffer to screen buffer
                mTileBuffer = (Bitmap)mUpdateBuffer.Clone();

                // raise map view updated event
                MapViewUpdated();

                GC.Collect(); // reclaim memory from creating bitmaps
            }
            catch (Exception ex)
            {
                if (OnMapError != null)
                {
                    OnMapError(new MapException(ex, "Error updating map view.", "VirtualEarthMap", "VirtualEarthMap_TileBufferUpdated()"), true);
                }
            }
        }

        #region Map Movement
        
        public bool ZoomIn()
        {
            if (!mTileUpdatePending && mDetailLevelIndex < mMapInfo.DetailLevels.Length - 1)
            {
                // increment detail level index
                mDetailLevelIndex++;

                // adjust pixel position for new detail level
                mCurrentPixelX = mCurrentPixelX * 2;
                mCurrentPixelY = mCurrentPixelY * 2;

                // update the tile buffer
                startGetTileBuffer(mCurrentPixelX, mCurrentPixelY, mMapInfo.DetailLevels[mDetailLevelIndex]);

                return true;
            }

            // max zoom
            return false;
        }

        public bool ZoomOut()
        {
            if (!mTileUpdatePending && mDetailLevelIndex > 0)
            {
                // decrement the detail level index
                mDetailLevelIndex--;

                // adjust pixel position for new detail level
                mCurrentPixelX = mCurrentPixelX / 2;
                mCurrentPixelY = mCurrentPixelY / 2;

                // update the tile buffer
                startGetTileBuffer(mCurrentPixelX, mCurrentPixelY, mMapInfo.DetailLevels[mDetailLevelIndex]);

                return true;
            }

            // min zoom
            return false;
        }

        public bool GoToPosition(int pixelX, int pixelY)
        {
            try
            {
                // check if position is out of bounds
                if (!positionOutOfBounds(pixelX, pixelY))
                {
                    // set new pixel position
                    mCurrentPixelX = pixelX;
                    mCurrentPixelY = pixelY;

                    // set the area of tile buffer to show on the screen
                    setScreenDisplayArea();

                    // check if tile buffer update needed
                    if (!checkScreenBufferZone())
                    {
                        // update the tile buffer
                        startGetTileBuffer(mCurrentPixelX, mCurrentPixelY, mMapInfo.DetailLevels[mDetailLevelIndex]);
                    }

                    if (MapViewUpdated != null)
                    {
                        // raise map updated event
                        MapViewUpdated();
                    }

                    return true;
                }
            }

            catch (Exception ex)
            {
                throw new MapException(ex, "Error updating map view.", "VirtualEarthMap", "GoTo()");
            }
            catch
            {
                throw new Exception("Error updating map view.");
            }

            // position not available on map
            return false;
        }

        public bool GoToPosition(double longitude, double latitude)
        {
            int pixelX = -1;
            int pixelY = -1;

            // convert the center long/lat into pixel coordinates
            VirtualEarthTileSystem.LatLongToPixelXY(latitude, longitude, mMapInfo.DetailLevels[mDetailLevelIndex], out pixelX, out pixelY);

            // try to go to the specified position
            return GoToPosition(pixelX, pixelY);
        }

        public bool MoveLeft(int pixels)
        {
            try
            {
                // check if movement will go out of bounds
                if (!positionOutOfBounds(mCurrentPixelX - pixels, mCurrentPixelY))
                {
                    // change pixel position
                    mCurrentPixelX -= pixels;

                    // set the area of tile buffer to show on the screen
                    setScreenDisplayArea();

                    // check if a tile buffer update is needed
                    if (checkScreenBufferZone())
                    {
                        return true;
                    }

                    // update the tile buffer
                    startGetTileBuffer(mCurrentPixelX - pixels, mCurrentPixelY, mMapInfo.DetailLevels[mDetailLevelIndex]);
                    return true;
                }
                else if (!positionOutOfBounds(mCurrentPixelX - 1, mCurrentPixelY))
                {
                    // change pixel position
                    mCurrentPixelX = mDetailLevelPixelBounds.X;

                    // set the area of tile buffer to show on the screen
                    setScreenDisplayArea();

                    return true;
                }
            }
            catch
            {
                throw new Exception("Error updating map view.");
            }

            return false;
        }

        public bool MoveLeft()
        {
            try
            {
                if (!positionOutOfBounds(mCurrentPixelX - M_MAP_SHIFT_STEP, mCurrentPixelY))
                {
                    // change pixel position
                    mCurrentPixelX -= M_MAP_SHIFT_STEP;

                    // set the area of tile buffer to show on the screen
                    setScreenDisplayArea();

                    // check if a tile buffer update is needed
                    if (checkScreenBufferZone())
                    {
                        return true;
                    }

                    // update the tile buffer
                    startGetTileBuffer(mCurrentPixelX, mCurrentPixelY, mMapInfo.DetailLevels[mDetailLevelIndex]);

                    return true;
                }
                else if (!positionOutOfBounds(mCurrentPixelX - 1, mCurrentPixelY))
                {
                    // change pixel position
                    mCurrentPixelX = mDetailLevelPixelBounds.X;

                    // set the area of tile buffer to show on the screen
                    setScreenDisplayArea();

                    return true;
                }

                return false;
            }
            catch
            {
                throw new Exception("Error updating map view.");
            }
        }

        public bool MoveRight(int pixels)
        {
            try
            {
                if (!positionOutOfBounds(mCurrentPixelX + pixels, mCurrentPixelY))
                {
                    // change pixel position
                    mCurrentPixelX += pixels;

                    // set the area of tile buffer to show on the screen
                    setScreenDisplayArea();

                    // check if a tile buffer update is needed
                    if (checkScreenBufferZone())
                    {
                        return true;
                    }

                    // update the tile buffer
                    startGetTileBuffer(mCurrentPixelX, mCurrentPixelY, mMapInfo.DetailLevels[mDetailLevelIndex]);

                    return true;
                }
                else if (!positionOutOfBounds(mCurrentPixelX + 1, mCurrentPixelY))
                {
                    // change pixel position
                    mCurrentPixelX = mDetailLevelPixelBounds.Right - 1;

                    // set the area of tile buffer to show on the screen
                    setScreenDisplayArea();

                    return true;
                }

                return false;
            }
            catch
            {
                throw new Exception("Error updating map view.");
            }
        }

        public bool MoveRight()
        {
            try
            {
                if (!positionOutOfBounds(mCurrentPixelX + M_MAP_SHIFT_STEP, mCurrentPixelY))
                {
                    // change pixel position
                    mCurrentPixelX += M_MAP_SHIFT_STEP;

                    // set the area of tile buffer to show on the screen
                    setScreenDisplayArea();

                    // check if a tile buffer update is needed
                    if (checkScreenBufferZone())
                    {
                        return true;
                    }

                    // update the tile buffer
                    startGetTileBuffer(mCurrentPixelX, mCurrentPixelY, mMapInfo.DetailLevels[mDetailLevelIndex]);

                    return true;
                }
                else if (!positionOutOfBounds(mCurrentPixelX + 1, mCurrentPixelY))
                {
                    // change pixel position
                    mCurrentPixelX = mDetailLevelPixelBounds.Right - 1;

                    // set the area of tile buffer to show on the screen
                    setScreenDisplayArea();

                    return true;
                }

                return false;
            }
            catch
            {
                throw new Exception("Error updating map view.");
            }
        }

        public bool MoveUp(int pixels)
        {
            return false;
        }

        public bool MoveUp()
        {
            try
            {
                if (!positionOutOfBounds(mCurrentPixelX, mCurrentPixelY - M_MAP_SHIFT_STEP))
                {
                    // change pixel position
                    mCurrentPixelY -= M_MAP_SHIFT_STEP;

                    // set the area of tile buffer to show on the screen
                    setScreenDisplayArea();

                    // check if a tile buffer update is needed
                    if (checkScreenBufferZone())
                    {
                        return true;
                    }

                    // update the tile buffer
                    startGetTileBuffer(mCurrentPixelX, mCurrentPixelY, mMapInfo.DetailLevels[mDetailLevelIndex]);

                    return true;
                }
                else if (!positionOutOfBounds(mCurrentPixelX, mCurrentPixelY - 1))
                {
                    // change pixel position
                    mCurrentPixelY = mDetailLevelPixelBounds.Y;

                    // set the area of tile buffer to show on the screen
                    setScreenDisplayArea();

                    return true;
                }

                return false;
            }
            catch
            {
                throw new Exception("Error updating map view.");
            }
        }

        public bool MoveDown(int pixels)
        {
            return false;
        }

        public bool MoveDown()
        {
            try
            {
                if (!positionOutOfBounds(mCurrentPixelX, mCurrentPixelY + M_MAP_SHIFT_STEP))
                {
                    // change pixel position
                    mCurrentPixelY += M_MAP_SHIFT_STEP;

                    // set the area of tile buffer to show on the screen
                    setScreenDisplayArea();

                    // check if a tile buffer update is needed
                    if (checkScreenBufferZone())
                    {
                        return true;
                    }

                    // update the tile buffer
                    startGetTileBuffer(mCurrentPixelX, mCurrentPixelY, mMapInfo.DetailLevels[mDetailLevelIndex]);

                    return true;
                }
                else if (!positionOutOfBounds(mCurrentPixelX, mCurrentPixelY + 1))
                {
                    // change pixel position
                    mCurrentPixelY = mDetailLevelPixelBounds.Bottom - 1;

                    // set the area of tile buffer to show on the screen
                    setScreenDisplayArea();

                    return true;
                }

                return false;
            }
            catch
            {
                throw new Exception("Error updating map view.");
            }
        }
        #endregion

        #region Screen Positioning

        public bool PositionOutOfBounds(double longitude, double latitude)
        {
            int pixelX = -1;
            int pixelY = -1;

            // get pixel coordinates for latitude and longitude
            VirtualEarthTileSystem.LatLongToPixelXY(latitude, longitude, mMapInfo.DetailLevels[mDetailLevelIndex], out pixelX, out pixelY);

            // check if out of bounds
            return positionOutOfBounds(pixelX, pixelY);
        }

        private bool positionOutOfBounds(int pixelX, int pixelY)
        {
            // check if pixel coordinate lies in map bounds
            if (mDetailLevelPixelBounds.Contains(pixelX, pixelY))
            {
                return false;
            }

            return true;
        }

        private bool checkScreenBufferZone()
        {
            try
            {
                // screen buffer zone lies entirely inside tile buffer
                if (Rectangle.Intersect(mScreenBufferZone, mTileBufferRectangle) == mScreenBufferZone)
                {
                    // no tile buffer update needed
                    return true;
                }

                // check if screen buffer zone is inside left edge of the map
                if (mCurrentPixelX - mDetailLevelPixelBounds.X >= 0 &&
                         mCurrentPixelX - mDetailLevelPixelBounds.X <= mScreenBufferZone.Width / 2)
                {
                    // check if screen buffer zone is inside top or bottom edge of the map
                    if (mScreenBufferZone.Top <= mTileBufferRectangle.Top &&
                        mDetailLevelPixelBounds.Top <= mCurrentPixelY - (mScreenBufferZone.Height / 2))
                    {
                        // buffer zone is inside top edge of the map, update tile buffer
                        return false;
                    }
                    else if (mScreenBufferZone.Bottom >= mTileBufferRectangle.Bottom &&
                        mDetailLevelPixelBounds.Bottom >= mCurrentPixelY + (mScreenBufferZone.Height / 2))
                    {
                        // buffer zone is inside bottom edge of the map, update tile buffer
                        return false;
                    }

                    // no tile buffer update needed
                    return true;
                }

                // check if screen buffer zone is inside right edge of the map
                if ((mDetailLevelPixelBounds.Right - 1) - mCurrentPixelX >= 0 &&
                         (mDetailLevelPixelBounds.Right - 1) - mCurrentPixelX <= mScreenBufferZone.Width / 2)
                {
                    // check if screen buffer zone is inside top or bottom edge of the map
                    if (mScreenBufferZone.Top <= mTileBufferRectangle.Top &&
                        mDetailLevelPixelBounds.Top <= mCurrentPixelY - (mScreenBufferZone.Height / 2))
                    {
                        // buffer zone is inside top edge of the map, update tile buffer
                        return false;
                    }
                    else if (mScreenBufferZone.Bottom >= mTileBufferRectangle.Bottom &&
                             mDetailLevelPixelBounds.Bottom >= mCurrentPixelY + (mScreenBufferZone.Height / 2))
                    {
                        // buffer zone is inside bottom edge of the map, update tile buffer
                        return false;
                    }

                    // no tile buffer update needed
                    return true;
                }

                // check if screen buffer zone is inside top edge of the map
                if (mCurrentPixelY - mDetailLevelPixelBounds.Y >= 0 &&
                         mCurrentPixelY - mDetailLevelPixelBounds.Y <= mScreenBufferZone.Height / 2)
                {
                    // check if screen buffer zone is inside top or bottom edge of the map
                    if (mScreenBufferZone.Left <= mTileBufferRectangle.Left &&
                        mDetailLevelPixelBounds.Left <= mCurrentPixelX - (mScreenBufferZone.Width / 2))
                    {
                        // buffer zone is inside left edge of the map, update tile buffer
                        return false;
                    }
                    else if (mScreenBufferZone.Right >= mTileBufferRectangle.Right &&
                             mDetailLevelPixelBounds.Right >= mCurrentPixelX + (mScreenBufferZone.Width / 2))
                    {
                        // buffer zone is inside right edge of the map, update tile buffer
                        return false;
                    }

                    // no tile buffer update needed
                    return true;
                }

                // check if screen buffer zone is inside bottom edge of the map
                if ((mDetailLevelPixelBounds.Bottom - 1) - mCurrentPixelY >= 0 &&
                         (mDetailLevelPixelBounds.Bottom - 1) - mCurrentPixelY <= mScreenBufferZone.Height / 2)
                {
                    // check if screen buffer zone is inside top or bottom edge of the map
                    if (mScreenBufferZone.Left <= mTileBufferRectangle.Left &&
                        mDetailLevelPixelBounds.Left <= mCurrentPixelX - (mScreenBufferZone.Width / 2))
                    {
                        // buffer zone is inside left edge of the map, update tile buffer
                        return false;
                    }
                    else if (mScreenBufferZone.Right >= mTileBufferRectangle.Right &&
                             mDetailLevelPixelBounds.Right >= mCurrentPixelX + (mScreenBufferZone.Width / 2))
                    {
                        // buffer zone is inside right edge of the map, update tile buffer
                        return false;
                    }

                    // no tile buffer update needed
                    return true;
                }

                // update tile buffer
                return false;
            }
            catch
            {
                throw new Exception("Error updating map view.");
            }
        }

        private void setScreenDisplayArea()
        {
            try
            {
                // calculate pixel position in relation to tile buffer bounds
                int posX = mCurrentPixelX - mTileBufferPixelBounds.X;
                int posY = mCurrentPixelY - mTileBufferPixelBounds.Y;

                // set screen buffer position
                mScreenBufferZone.X = posX - ((mScreenRectangle.Width / 2) + SCREEN_BUFFER_ZONE_SIZE);
                mScreenBufferZone.Y = posY - ((mScreenRectangle.Height / 2) + SCREEN_BUFFER_ZONE_SIZE);
                mScreenBufferZone.Width = mScreenRectangle.Width + (SCREEN_BUFFER_ZONE_SIZE * 2);
                mScreenBufferZone.Height = mScreenRectangle.Height + (SCREEN_BUFFER_ZONE_SIZE * 2);

                // set screen view position
                mScreenDisplayArea.X = posX - (mScreenRectangle.Width / 2);
                mScreenDisplayArea.Y = posY - (mScreenRectangle.Height / 2);
                mScreenDisplayArea.Width = mScreenRectangle.Width;
                mScreenDisplayArea.Height = mScreenRectangle.Height;
            }
            catch
            {
                throw new Exception("Error updating map view.");
            }
        }
        #endregion
    }
}
